#include <windows.h>
#include "log/logger.h"

namespace qklog {

uHandle WinApi_GetStdHandle()
{
    return ::GetStdHandle(STD_OUTPUT_HANDLE);
}

std::string logger::get_default_log_prefiex(qklog::level lvl) {
    // add [2022-06-01 01:01:01]
    auto now = log_clock::now();
    uint64_t dis_millseconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count()
                               - std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
    time_t tt = std::chrono::system_clock::to_time_t(now);
    auto time_tm = localtime(&tt);
    char timeStamp[27] = {'\0'};
    sprintf(timeStamp, "[%d-%02d-%02d %02d:%02d:%02d.%03u]", time_tm->tm_year + 1900,
            time_tm->tm_mon + 1, time_tm->tm_mday, time_tm->tm_hour,
            time_tm->tm_min, time_tm->tm_sec, dis_millseconds);

    std::string prefiex(timeStamp);

    // add [xxxx]
    switch (lvl)
    {
    case level::trace :
        prefiex += " [  trace ] ";        
        break;
    case level::debug :
        prefiex += " [  debug ] ";
        break;
    case level::info :
        prefiex += " [  info  ] ";
        break;
    case level::warn :
        prefiex += " [  warn  ] ";
        break;
    case level::error :
        prefiex += " [   error ] ";
        break;
    default:
        break;
    }

    return prefiex;
}

std::unordered_map<qklog::level, uint32> stdout_logger::colors  {
        {qklog::level::trace, FOREGROUND_BLUE},                                         // blue
        {qklog::level::debug, FOREGROUND_GREEN | FOREGROUND_BLUE},                      // cyan
        {qklog::level::info, FOREGROUND_GREEN},                                         // green
        {qklog::level::warn, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY}, // intensity yellow
        {qklog::level::error, FOREGROUND_RED | FOREGROUND_INTENSITY},                   // intensity red
        {qklog::level::off, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE}        // white
};

void stdout_logger::print_range(const char* buf, int l, int r, uint32 color) {
    CONSOLE_SCREEN_BUFFER_INFO orig_buffer_info;
    ::GetConsoleScreenBufferInfo(stdout_handler, &orig_buffer_info);
    
    ::SetConsoleTextAttribute(stdout_handler, color);
    ::WriteConsoleA(stdout_handler, buf + l, r - l + 1, nullptr, nullptr);

    ::SetConsoleTextAttribute(stdout_handler, orig_buffer_info.wAttributes);
}

void stdout_logger::write_log(qklog::level lvl, const std::string& fmt, ...) {
    std::string log_msg(logger::get_default_log_prefiex(lvl));
    char buf[1024];
    memset(buf, '\0', sizeof(buf));
    va_list v;
    va_start(v, fmt);
    int n = vsnprintf(buf, sizeof(buf), fmt.c_str(), v);
    
    int color_l, color_r;
    for (int i = log_msg.size() - 1; i >= 0; i--) {
        if (log_msg[i] == ']') color_r = i;
        if (log_msg[i] == '[') {
            color_l = i;
            break;
        }
    }

     print_range(log_msg.c_str(), 0, color_l, colors[level::off]);
     print_range(log_msg.c_str(), color_l + 1, color_r - 1, colors[lvl]);
     print_range(log_msg.c_str(), color_r, log_msg.size() - 1, colors[level::off]);
     print_range(buf, 0, n - 1, colors[lvl]);
     putchar('\n');
}

};